package epam.com.rd.MyCollections;

import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;

/**
 * 
 * http://www.sql.ru/forum/710113/realizaciya-iterator
 * http://stackoverflow.com/questions
 * /15993356/how-iterators-remove-method-actually-remove-an-object
 */
public class MyArrayList implements MyList, RandomAccess {

	private static final int DEFAULT_CAPACITY = 10;
	private static final Object[] EMPTYDATA = {};
	private transient Object[] data;
	private int size;

	public MyArrayList() {
		this.data = EMPTYDATA;
	}

	public MyArrayList(MyList c) {
		data = c.toArray();
		size = data.length;
	}

	public MyArrayList(int initialCapacity) {
		if (initialCapacity < 0) {
			throw new IllegalArgumentException();
		}
		this.data = new Object[initialCapacity];
		this.size = initialCapacity;
	}

	public void ensureCapacity(int minCapacity) {
		int minExpand = (data != EMPTYDATA) ? 0 : DEFAULT_CAPACITY;
		if (minCapacity > minExpand) {
			if (minCapacity - data.length > 0) {
				int oldCapacity = data.length;
				int newCapacity = oldCapacity + (oldCapacity / 2);
				if (newCapacity - minCapacity < 0)
					newCapacity = minCapacity;
				data = Arrays.copyOf(data, newCapacity);
			}

		}
	}

	private void ensureCapacityInternal(int minCapacity) {
		if (data == EMPTYDATA) {
			minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
		}
		if (minCapacity - data.length > 0) {
			int oldCapacity = data.length;
			int newCapacity = oldCapacity + (oldCapacity / 2);
			if (newCapacity - minCapacity < 0)
				newCapacity = minCapacity;
			data = Arrays.copyOf(data, newCapacity);
		}

	}

	public void trimToSize() {
		if (size < data.length) {
			this.data = Arrays.copyOf(data, size);
		}
	}

	public void add(Object e) {
		ensureCapacityInternal(size + 1);
		data[size++] = e;

	}

	public void add(int index, Object e) {
		if (index > size || index < 0)
			throw new MyIndexOutOfBoundsException();

		ensureCapacityInternal(size + 1); // Increments modCount!!
		System.arraycopy(data, index, data, index + 1, size - index);
		data[index] = e;
		size++;

	}

	public void addAll(Object[] a) {
		int numNew = a.length;
		ensureCapacityInternal(size + numNew);
		System.arraycopy(a, 0, data, size, numNew);
		size += numNew;
	}

	public void addAll(int index, Object[] a) {
		if (index > size || index < 0)
			throw new MyIndexOutOfBoundsException("Index out of bounds");

		int numNew = a.length;
		ensureCapacityInternal(size + numNew);
		int numMoved = size - index;
		if (numMoved > 0)
			System.arraycopy(data, index, data, index + numNew, numMoved);

		System.arraycopy(a, 0, data, index, numNew);
		size += numNew;

	}

	public Object get(int index) {
		if (index >= size || index < 0) {
			throw new MyIndexOutOfBoundsException("Index" + index
					+ "out of bounds");
		} else
			return data[index];
	}

	public Object remove(int index) {
		if (index >= size)
			throw new MyIndexOutOfBoundsException("Index" + index
					+ "out of bounds");
		if (index < 0)
			throw new MyIndexOutOfBoundsException("Index" + index
					+ "out of bounds");

		Object oldValue = data[index];

		int numMoved = size - index - 1;
		if (numMoved > 0)
			System.arraycopy(data, index + 1, data, index, numMoved);
		data[--size] = null;
		return oldValue;
	}

	public void clear() {
		for (int i = 0; i < size; i++)
			data[i] = null;
		size = 0;
	}

	public boolean isEmpty() {
		return size == 0;
	}

	public void set(int index, Object e) {
		if (index >= size || index < 0)
			throw new MyIndexOutOfBoundsException("Index" + index
					+ "out of bounds");
		data[index] = e;
	}

	public int indexOf(Object o) {
		if (o == null) {
			for (int i = 0; i < size; i++)
				if (data[i] == null)
					return i;
		} else {
			for (int i = 0; i < size; i++)
				if (o.equals(data[i]))
					return i;
		}
		return -1;
	}

	public int size() {
		return size;
	}

	public Object[] toArray() {
		Object c[];
		c = Arrays.copyOf(data, size);
		return c;
	}

	public Iterator<Object> iterator() {
		return new Iterator<Object>() {
			int cursor;
			int lastRet = -1;

			public boolean hasNext() {
				return cursor != size;
			}

			public Object next() {
				int i = cursor;
				if (i >= size)
					throw new NoSuchElementException();
				Object[] elementData = MyArrayList.this.data;

				cursor = i + 1;
				return elementData[lastRet = i];
			}

			public void remove() {

				try {
					MyArrayList.this.remove(lastRet);
					cursor = lastRet;
					lastRet = -1;
				} catch (IndexOutOfBoundsException ex) {

				}
			}

		};
	}

}
